from string import Template
from datetime import timedelta
from concurrent.futures import ThreadPoolExecutor

from django.db.models import QuerySet
from django.db import transaction
from django.utils import timezone as dj_timezone

from apps.app_tx_meeting.managers import tx_meeting_configs
from apps.app_tx_meeting.helpers import TxMeetingEmailHandle
from apps.app_tx_meeting.models import MeetingReservation, MeetingReservationLog
from apps.app_tx_meeting.notice_templates import build_utc8_time_range_content
from apps.app_users.models import Email
from core.loggers import config_script_logger


class TxMeetingNoticeEmail:
    """
    邮件通知
    """
    def __init__(self):
        self.logger = config_script_logger(
            name='timer_tx_meet_send_notice_email_logger', filename='timer_tx_meet_send_notice_email.log', stdout=False)

        self.email_handle = TxMeetingEmailHandle()
        self.notice_email_template = Template(tx_meeting_configs.get(tx_meeting_configs.ConfigName.PRE_MEET_NOTICE_TEMPLATE.value))

    @staticmethod
    def add_log(operate_status: str, content: str, reservation_id: str):
        try:
            MeetingReservationLog.add_log(
                log_type=MeetingReservationLog.LogType.NOTICE.value,
                operate_status=operate_status, content=content, reservation_id=reservation_id
            )
        except Exception as e:
            pass

    @staticmethod
    def define_time_range_format(start_timestamp, end_timestamp):
        """
        时间范围格式
        """
        return build_utc8_time_range_content(start_timestamp=start_timestamp, end_timestamp=end_timestamp)

    @staticmethod
    def build_notice_email_message(template: Template, tx_meeting: MeetingReservation) -> str:
        """
        通知内容构建
        """

        time_format = TxMeetingNoticeEmail.define_time_range_format(
            start_timestamp=tx_meeting.start_time, end_timestamp=tx_meeting.end_time)

        content = template.safe_substitute(
            meeting_userid=tx_meeting.tx_meeting_userid,
            meeting_subject=tx_meeting.subject,
            meeting_time=time_format,
            meeting_code=tx_meeting.tx_meeting_code,
            meeting_join_url=tx_meeting.tx_join_url,
        )

        return content

    def need_save_email_and_send(self, tx_meeting: MeetingReservation, email_conf):
        """
        发送腾讯会议通知邮件
        """

        message = self.build_notice_email_message(template=self.notice_email_template, tx_meeting=tx_meeting)

        try:
            with transaction.atomic():
                email = self.email_handle.save_email(
                    subject='科技云会（腾讯版）会议开始通知', receivers=[tx_meeting.tx_meeting_userid],
                    message=message, tag=Email.Tag.TX_MEETING.value,
                    email_conf=email_conf)

                tx_meeting.email_notice = email
                tx_meeting.save(update_fields=['email_notice'])
        except Exception as e:
            raise e

        if email:
            return self.email_handle.send_email(email=email, receivers=[tx_meeting.tx_meeting_userid])

    @staticmethod
    def get_need_notice_meetings() -> QuerySet:
        """ 需要发送通知邮件 """

        timestamp = int(dj_timezone.now().timestamp())
        # 加上开始时间大于当前时间条件
        return MeetingReservation.objects.filter(
            status=MeetingReservation.Status.RESERVED.value,
            need_notice_time__lte=timestamp, email_notice__isnull=True,
            start_time__gt=timestamp
        )

    @staticmethod
    def get_need_retry_failed_emails():
        """ 查询最近三个小时内发送失败需要重试通知邮件 """

        dt_3hours_ago = dj_timezone.now() - timedelta(hours=3)
        return Email.objects.filter(
            tag=Email.Tag.TX_MEETING.value,
            send_time__gte=dt_3hours_ago,
            status__in=[Email.Status.WAIT.value, Email.Status.FAILED.value],
            success_time__isnull=True
        ).order_by('-send_time')[:200]

    def task_send_email(self, email: Email):
        self.email_handle.send_email(email=email)

    def retry_send_failed_emails(self):
        email_qs = self.get_need_retry_failed_emails()
        emails = list(email_qs)
        if not emails:
            return

        with ThreadPoolExecutor() as executor:
            for email in emails:
                executor.submit(self.task_send_email, email=email)

            # 等待所有任务完成
            executor.shutdown(wait=True)

    def run(self):

        email_conf = TxMeetingEmailHandle().get_email_configs()
        # 邮件地址或密码未配置或密码太短不合理时，直接返回
        if not email_conf.host_user or not email_conf.host_password or len(email_conf.host_password) < 3:
            return

        tx_meetings = self.get_need_notice_meetings()

        for tx_meeting in tx_meetings:
            try:
                self.need_save_email_and_send(tx_meeting=tx_meeting, email_conf=email_conf)
            except Exception as e:
                self.logger.error(f'tx meeting (id={tx_meeting.id}) save email fail: {str(e)}')

        self.retry_send_failed_emails()
